<?php

namespace app\common\service;

use app\common\cachemodel\AppConfig;
use app\common\cachemodel\chat\call\HzCallChatRecordDetails;
use app\common\cachemodel\chat\hz\HzHzQuestion;
use app\common\cachemodel\chat\hz\HzHzQuestionLogic;
use app\common\cachemodel\chat\hz\HzHzQuestionText;
use app\common\cachemodel\chat\HzChatRecordDetails;
use app\common\cachemodel\chat\HzQuestion;
use app\common\cachemodel\chat\HzQuestionLogic;
use app\common\cachemodel\chat\HzQuestionText;
use app\common\lib\astrolog\Astrolog;
use app\common\lib\Util;
use app\common\lib\util\RecordTrain;
use GuzzleHttp\Exception\GuzzleException;
use think\Db;
use think\Env;
use think\Log;
use XznsTools\curl\Curl;

class ChatParse
{
    /**
     * @param int $type 类型 1=>个人起名 2=>公司起名
     * @param array $chatParams
     * @return array
     */
    public static function getCreateNameResultData(int $type, array $chatParams)
    {
        if ($type === 1)
        {
            $url = Env::get('haozhungpt.url') . '/api/get_naming_result';
        } else
        {
            $url = Env::get('haozhungpt.url') . '/api/get_company_naming_result';
        }

        $resData = Curl::request($url, $chatParams, 'POST', [], 'json');

        return $resData;
    }

    public static function getNameAnalyzeResultData(array $chatParams)
    {
        $url = Env::get('haozhungpt.url') . '/api/get_name_score_result';

        $resData = Curl::request($url, $chatParams, 'POST', [], 'json');

        return $resData;
    }

    public static function getNameAnalyzeWuxingStreamData(array $chatParams)
    {
        $url = Env::get('haozhungpt.url') . '/api/get_name_score_bazi_result_stream';
        // 这行代码用于关闭输出缓冲。关闭后，脚本的输出将立即发送到浏览器，而不是等待缓冲区填满或脚本执行完毕。
//        ini_set('output_buffering', 'off');
        // 这行代码禁用了 zlib 压缩。通常情况下，启用 zlib 压缩可以减小发送到浏览器的数据量，但对于服务器发送事件来说，实时性更重要，因此需要禁用压缩。
//        ini_set('zlib.output_compression', false);
        // 这行代码使用循环来清空所有当前激活的输出缓冲区。ob_end_flush() 函数会刷新并关闭最内层的输出缓冲区，@ 符号用于抑制可能出现的错误或警告。
        try
        {
            while (ob_get_level() > 0 && @ob_end_flush())
            {
            }
        }
        catch (\Exception $e)
        {
//            \think\Log::write(__CLASS__ . ' => ' . __FUNCTION__ . '()异常：' . $e->getMessage() . '失败信息：' . $e->getFile() . ':' . $e->getLine(), 'error', true);
        }
        ignore_user_abort(true); // 忽略客户端断开连接
        // 这行代码设置 HTTP 响应的 Content-Type 为 text/event-stream，这是服务器发送事件（SSE）的 MIME 类型。
        header('Content-Type: text/event-stream');
        // 这行代码设置 HTTP 响应的 Cache-Control 为 no-cache，告诉浏览器不要缓存此响应。
        header('Cache-Control: no-cache');
        // 这行代码设置 HTTP 响应的 Connection 为 keep-alive，保持长连接，以便服务器可以持续发送事件到客户端。
        header('Connection: keep-alive');

        header('X-Accel-Buffering: no');//这一行必须存在，否则不好使（X-Accel-Buffering 头信息设置为“no”，表示已经禁用缓冲机制，服务器会立即发送响应数据，以确保客户端能够尽早地接收到数据。而如果设置为“yes”或不设置该头信息，则表示启用缓冲机制，服务器会将响应数据写入缓冲区，然后批量发送给客户端。)
        // 创建一个 HTTP Client 实例
        $resData = Curl::requestStream($url, $chatParams, 'POST', ['Content-Type' => 'application/json', 'stream' => true], static function ($stream)
        {
            $resStr = '';
            while (!$stream->eof())
            {
                $res = $stream->read(1); // 每次读取 1 字节，可以根据需求进行调整
                echo $res; // 输出一波数据
                flush(); //刷新缓存（直接发送到浏览器）
                $resStr .= $res;
            }
            return $resStr;
        }, 'json');

        if ($resData['success'])
        {
            return $resData['data'];
        }
        return '';
    }

    public static function getNamePinyin(string $name)
    {
        $url = Env::get('haozhungpt.url') . '/api/get_name_pinyin';

        $resData = Curl::request($url, ['name' => $name], 'POST', [], 'json');

        return $resData;
    }

    public static function getAcquiredAstroCalculateData(array $chatParams)
    {
        $url = Env::get('haozhungpt.url') . '/api/send_acquired_constellation';

        $resData = Curl::request($url, $chatParams, 'POST', [], 'json');

        return $resData;
    }

    /**
     * 获取 问题语义解析结果
     * @return array|false[]
     * @throws GuzzleException
     */
    public static function getReocdeStrParse($recodeStr)
    {
        $url = Env::get('haozhungpt.url') . '/api/parse_record_info';

        $chatParams = [
            'str' => $recodeStr,
        ];
        $resData = Curl::request($url, $chatParams, 'POST', [], 'json');

        return $resData;
    }

    /**
     * 获取 问题语义解析结果
     * @param $question
     * @param $recodeInfo
     * @param $chatId
     * @param $gptModel
     * @param $isRecommend
     * @param array|null $recodeInfo2
     * @return array|false[]
     * @throws GuzzleException
     */
    public static function getQuestionParse($question, $recodeInfo, $chatId, $gptModel, $isRecommend, $recodeInfo2 = null, int $isCall = 0)
    {
        $url = Env::get('haozhungpt.url') . '/api_bot';

        $chatConfig = (new AppConfig())->configInfo('chat_config');

        $recodeChooseSwitch = (int)($chatConfig['recode_choose_switch'] ?? 0);
        $synastryRecodeChooseSwitch = (int)($chatConfig['synastry_recode_choose_switch'] ?? 0);
        $useZhipuOrMoonshot = (int)($chatConfig['use_zhipu_or_moonshot'] ?? 0);
        $isStream = (int)($chatConfig['is_stream'] ?? 0);

        $historyNum = (int)$chatConfig['history_num'];
        $historyData = [];
        if ($historyNum > 0)
        {   // 取用户最近会话 信息
            $historyData = (new HzChatRecordDetails())->getChatQuestionAnswerHistory($chatId, $historyNum, false, 0, $isCall);
        }
        // 获取上一次会话的时间，用于模型处理是否在开头增加欢迎语
        $lasttime = (new HzChatRecordDetails())->getChatQuestionAnswerLasttime($chatId);

        $chatParams = [
            'query' => $question,
            'stream' => $isStream === 1 ? 1 : 0,
            'history' => $historyData,
            'rid' => $recodeInfo['id'] ?? 0,
            'record_info' => (new RecordTrain())->format($recodeInfo),
            'max_length' => (int)$chatConfig['max_length'],
            'model_name' => $gptModel,
            'last_time' => (string)$lasttime,
            'is_question_recommand' => $isRecommend === 1 ? 1 : 0,
            'version' => request()->header('version', ''),
            'app_type' => 1,
            'rid1' => $recodeInfo2['id'] ?? 0,    // 副档案ID  当该值为0的情况下属于单盘模式，当该值非0情况下属于双盘模式
            'rid_name' => $recodeInfo['name'],
            'rid1_name' => $recodeInfo2['name'] ?? '',
            'two_rid_response_type' => $recodeChooseSwitch === 1 ? 1 : 0,
            'two_rid_is_combining_response_type' => $synastryRecodeChooseSwitch === 1 ? 1 : 0,
            'use_zhipu_or_moonshot' => $useZhipuOrMoonshot === 1 ? 1 : 0,
            'is_call' => $isCall,
        ];

        $resData = Curl::request($url, $chatParams, 'POST', [], 'json');

        if ($resData['success'] && $resData['data']['code'] === 200)
        {
            if (isset($resData['data']['data']['is_current_session_valid']) && (int)$resData['data']['data']['is_current_session_valid'] === 2)
            {
                $resData['data']['data']['stream'] = 1;
            } else if ($chatParams['stream'] === 1 && $resData['data']['data']['type'] === 2)
            {
                if ((isset($resData['data']['data']['is_show_need_record_hint']) && (int)$resData['data']['data']['is_show_need_record_hint'] === 1) ||
                    (isset($resData['data']['data']['is_current_session_valid']) && (int)$resData['data']['data']['is_current_session_valid'] === 0))
                {
                    //是否在App端提示用户需要档案信息 0=>不提示需要档案信息，1=>提示需要档案信息，当 is_show_need_record_hint=1的时候，应python要求，不能是流式输出
                    $resData['data']['data']['stream'] = 0;
                } else
                {
                    $resData['data']['data']['stream'] = 1;
                }
            } else
            {
                $resData['data']['data']['stream'] = 0;
            }

        } else
        {//非200的话，打印异常日志
            Log::write('python服务器返回异常：请求参数' . json_encode($chatParams, JSON_UNESCAPED_UNICODE) . '； 返回信息：' . json_encode($resData, JSON_UNESCAPED_UNICODE), 'error', true);
        }

        $resData['history'] = $historyData;
        $resData['question'] = $question;
        return $resData;
    }

    /**
     * 获取 问题语义解析结果
     * @param $question
     * @param $recodeInfo
     * @param $chatId
     * @param $gptModel
     * @param array|null $recodeInfo2
     * @return array|false[]
     * @throws GuzzleException
     */
    public static function getCallQuestionParse($question, $recodeInfo, $chatId, $gptModel, $recodeInfo2 = null)
    {
        $url = Env::get('haozhungpt.url') . '/api_bot';

        $chatConfig = (new AppConfig())->configInfo('chat_config');

        $recodeChooseSwitch = (int)($chatConfig['recode_choose_switch'] ?? 0);
        $synastryRecodeChooseSwitch = (int)($chatConfig['synastry_recode_choose_switch'] ?? 0);
        $useZhipuOrMoonshot = (int)($chatConfig['use_zhipu_or_moonshot'] ?? 0);
        $isStream = (int)($chatConfig['is_stream'] ?? 0);

        $historyNum = (int)$chatConfig['history_num'];
        $historyData = [];
        $hzCallChatRecordDetailsModel = new HzCallChatRecordDetails();
        if ($historyNum > 0)
        {   // 取用户最近会话 信息
            $historyData = $hzCallChatRecordDetailsModel->getChatQuestionAnswerHistory($chatId, $historyNum);
        }
        // 获取上一次会话的时间，用于模型处理是否在开头增加欢迎语
        $lasttime = $hzCallChatRecordDetailsModel->getChatQuestionAnswerLasttime($chatId);

        $chatParams = [
            'query' => $question,
            'stream' => $isStream === 1 ? 1 : 0,
            'history' => $historyData,
            'rid' => $recodeInfo['id'] ?? 0,
            'record_info' => (new RecordTrain())->format($recodeInfo),
            'max_length' => (int)$chatConfig['max_length'],
            'model_name' => $gptModel,
            'last_time' => (string)$lasttime,
            'is_question_recommand' => 0,
            'version' => request()->header('version', ''),
            'app_type' => 1,
            'rid1' => $recodeInfo2['id'] ?? 0,    // 副档案ID  当该值为0的情况下属于单盘模式，当该值非0情况下属于双盘模式
            'rid_name' => $recodeInfo['name'],
            'rid1_name' => $recodeInfo2['name'] ?? '',
            'two_rid_response_type' => $recodeChooseSwitch === 1 ? 1 : 0,
            'two_rid_is_combining_response_type' => $synastryRecodeChooseSwitch === 1 ? 1 : 0,
            'use_zhipu_or_moonshot' => $useZhipuOrMoonshot === 1 ? 1 : 0,
            'is_call' => 1,
        ];

        $resData = Curl::request($url, $chatParams, 'POST', [], 'json');

        if ($resData['success'] && $resData['data']['code'] === 200)
        {
            if (isset($resData['data']['data']['is_current_session_valid']) && (int)$resData['data']['data']['is_current_session_valid'] === 2)
            {
                $resData['data']['data']['stream'] = 1;
            } else if ($chatParams['stream'] === 1 && $resData['data']['data']['type'] === 2)
            {
                if ((isset($resData['data']['data']['is_show_need_record_hint']) && (int)$resData['data']['data']['is_show_need_record_hint'] === 1) ||
                    (isset($resData['data']['data']['is_current_session_valid']) && (int)$resData['data']['data']['is_current_session_valid'] === 0))
                {
                    //是否在App端提示用户需要档案信息 0=>不提示需要档案信息，1=>提示需要档案信息，当 is_show_need_record_hint=1的时候，应python要求，不能是流式输出
                    $resData['data']['data']['stream'] = 0;
                } else
                {
                    $resData['data']['data']['stream'] = 1;
                }
            } else
            {
                $resData['data']['data']['stream'] = 0;
            }

        } else
        {//非200的话，打印异常日志
            Log::write('python服务器返回异常：请求参数' . json_encode($chatParams, JSON_UNESCAPED_UNICODE) . '； 返回信息：' . json_encode($resData, JSON_UNESCAPED_UNICODE), 'error', true);
        }

        $resData['history'] = $historyData;
        $resData['question'] = $question;
        return $resData;
    }

    /**
     * 获取 问题语义解析结果 stream 流式数据结果
     * @param $question
     * @param $recodeInfo
     * @param $chatId
     * @param int $chatDetailId
     * @param $gptModel
     * @return string
     * @throws GuzzleException
     */
    public static function getQuestionParseStreamData($question, $recodeInfo, $chatId, $chatDetailId = 0, $gptModel = '', $parseData = [])
    {
        $url = Env::get('haozhungpt.url') . '/api/stream';

        $chatConfig = (new AppConfig())->configInfo('chat_config');

        $historyNum = (int)$chatConfig['history_num'];
        $historyData = [];
        if ($historyNum > 0 && (!isset($parseData['is_dice']) || $parseData['is_dice'] === 0))
        {   // 取用户最近会话 信息
            $historyData = (new HzChatRecordDetails())->getChatQuestionAnswerHistory($chatId, $historyNum, true, $chatDetailId, $parseData['is_call']);
        }
        // 获取上一次会话的时间，用于模型处理是否在开头增加欢迎语
        $lasttime = (new HzChatRecordDetails())->getChatQuestionAnswerLasttime($chatId);

        $chatParams = [
            'query' => $question,
            'stream' => 1,
            'history' => $historyData,
            'rid' => (int)$recodeInfo['id'],
            'record_info' => (new RecordTrain())->format($recodeInfo),
            'max_length' => (int)$chatConfig['max_length'],
            'model_name' => $gptModel,
            'last_time' => (string)$lasttime,
            'semantic_answer_scene' => $parseData['semantic_answer_scene'],
            'semantic_answer_cls_tools' => $parseData['semantic_answer_cls_tools'],
            'chatid' => $chatDetailId, // 会话记录ID
            'is_dice' => $parseData['is_dice'] ?? 0,
            'dice_info' => $parseData['dice_info'] ?? null,
            'version' => request()->header('version', ''),
            'app_type' => 1,
            'is_consum' => $parseData['is_consum'],
            'rid1' => $parseData['rid1'],
            'rid_name' => $recodeInfo['name'],
            'rid1_name' => $parseData['rid1_name'],
            'is_combining' => $parseData['is_combining'],
            'is_call' => $parseData['is_call'],
        ];


        // 这行代码用于关闭输出缓冲。关闭后，脚本的输出将立即发送到浏览器，而不是等待缓冲区填满或脚本执行完毕。
//        ini_set('output_buffering', 'off');

        // 这行代码禁用了 zlib 压缩。通常情况下，启用 zlib 压缩可以减小发送到浏览器的数据量，但对于服务器发送事件来说，实时性更重要，因此需要禁用压缩。
//        ini_set('zlib.output_compression', false);

        // 这行代码使用循环来清空所有当前激活的输出缓冲区。ob_end_flush() 函数会刷新并关闭最内层的输出缓冲区，@ 符号用于抑制可能出现的错误或警告。
        ignore_user_abort(true); // 忽略客户端断开连接
        ob_implicit_flush(true); // 开启隐式刷新
        try
        {
            while (ob_get_level() > 0 && @ob_end_flush())
            {
            }
        }
        catch (\Exception $e)
        {
        }
        // 这行代码设置 HTTP 响应的 Content-Type 为 text/event-stream，这是服务器发送事件（SSE）的 MIME 类型。
        header('Content-Type: text/event-stream');

        // 这行代码设置 HTTP 响应的 Cache-Control 为 no-cache，告诉浏览器不要缓存此响应。
        header('Cache-Control: no-cache');

        // 这行代码设置 HTTP 响应的 Connection 为 keep-alive，保持长连接，以便服务器可以持续发送事件到客户端。
        header('Connection: keep-alive');

        header('X-Accel-Buffering: no');//这一行必须存在，否则不好使（X-Accel-Buffering 头信息设置为“no”，表示已经禁用缓冲机制，服务器会立即发送响应数据，以确保客户端能够尽早地接收到数据。而如果设置为“yes”或不设置该头信息，则表示启用缓冲机制，服务器会将响应数据写入缓冲区，然后批量发送给客户端。)
        // 创建一个 HTTP Client 实例
//        $client = new Client([
//            'timeout' => 60 // 超时时间60秒
//        ]);
//
//        // 调用 request 方法发送请求
//        $response = $client->request("POST", $url, [
//            'headers' => [
//                'Content-Type' => 'application/json'
//            ],
//            'json' => $chatParams, // 设置请求参数
//            'stream' => true // 设置流式输出
//        ]);
//
//        // 输出响应体
//        $stream = $response->getBody();
//
//        $oldStr = '';
//        $resStr = '';
//        $flag = 0;
//        while (!$stream->eof())
//        {
//            $res = $stream->read(1); // 每次读取 1 字节，可以根据需求进行调整
//            if ($res === "\n")
//            {
//                $flag++;
//            } else
//            {
//                $flag = 0;
//            }
//            $resStr .= $res;
//            if ($flag >= 4)
//            {   // 检测到3个 \n\n\n 则将数据 输出到页面
//                $resStr = rtrim($resStr, "\n\n\n\n");
//                if ($resStr)
//                {
//                    usleep(50000);
//                    echo $resStr; // 输出一波数据
//                    flush(); //刷新缓存（直接发送到浏览器）
//                }
//                $oldStr = $resStr;
//                $resStr = ''; // 置空
//                $flag = 0;
//            }
//        }
        $resData = Curl::requestStream($url, $chatParams, 'POST', ['Content-Type' => 'application/json', 'stream' => true], function ($stream)
        {
            $resStr = '';
            while (!$stream->eof())
            {
                $res = $stream->read(1); // 每次读取 1 字节，可以根据需求进行调整
                echo $res; // 输出一波数据
                flush(); //刷新缓存（直接发送到浏览器）

                $resStr .= $res;
            }

            return $resStr;
        }, 'json');
        $resData['question'] = $question;
        $resData['chat_id'] = $chatId;
        $resData['chat_record_id'] = $chatDetailId;
        // 添加记录
        Db::table('wechat_backup_07_18_14_43_00.hz_chat_python_result_log')->insert(['chat_id' => $chatId, 'chat_record_id' => $chatDetailId, 'result' => json_encode($resData, JSON_UNESCAPED_UNICODE), 'create_time' => time()]);

        if ($resData['success'])
        {
            return $resData['data'];
        } else
        {
            return '';
        }
    }

    /**
     * 获取 问题语义解析结果 stream 流式数据结果
     * @param $question
     * @param $recodeInfo
     * @param $chatId
     * @param int $chatDetailId
     * @param $gptModel
     * @return string
     * @throws GuzzleException
     */
    public static function getCallQuestionParseStreamData($question, $recodeInfo, $chatId, $chatDetailId = 0, $gptModel = '', $parseData = [])
    {
        $url = Env::get('haozhungpt.url') . '/api/stream';

        $chatConfig = (new AppConfig())->configInfo('chat_config');

        $historyNum = (int)$chatConfig['history_num'];
        $historyData = [];
        $hzCallChatRecordDetailsModel = new HzCallChatRecordDetails();
        if ($historyNum > 0 && (!isset($parseData['is_dice']) || $parseData['is_dice'] === 0))
        {   // 取用户最近会话 信息
            $historyData = $hzCallChatRecordDetailsModel->getChatQuestionAnswerHistory($chatId, $historyNum, true, $chatDetailId);
        }
        // 获取上一次会话的时间，用于模型处理是否在开头增加欢迎语
        $lasttime = $hzCallChatRecordDetailsModel->getChatQuestionAnswerLasttime($chatId);

        $chatParams = [
            'query' => $question,
            'stream' => 1,
            'history' => $historyData,
            'rid' => (int)$recodeInfo['id'],
            'record_info' => (new RecordTrain())->format($recodeInfo),
            'max_length' => (int)$chatConfig['max_length'],
            'model_name' => $gptModel,
            'last_time' => (string)$lasttime,
            'semantic_answer_scene' => $parseData['semantic_answer_scene'],
            'semantic_answer_cls_tools' => $parseData['semantic_answer_cls_tools'],
            'chatid' => $chatDetailId, // 会话记录ID
            'is_dice' => $parseData['is_dice'] ?? 0,
            'dice_info' => $parseData['dice_info'] ?? null,
            'version' => request()->header('version', ''),
            'app_type' => 1,
            'is_consum' => $parseData['is_consum'],
            'rid1' => $parseData['rid1'],
            'rid_name' => $recodeInfo['name'],
            'rid1_name' => $parseData['rid1_name'],
            'is_combining' => $parseData['is_combining'],
            'is_call' => 1,
        ];


        // 这行代码用于关闭输出缓冲。关闭后，脚本的输出将立即发送到浏览器，而不是等待缓冲区填满或脚本执行完毕。
//        ini_set('output_buffering', 'off');

        // 这行代码禁用了 zlib 压缩。通常情况下，启用 zlib 压缩可以减小发送到浏览器的数据量，但对于服务器发送事件来说，实时性更重要，因此需要禁用压缩。
//        ini_set('zlib.output_compression', false);
        ignore_user_abort(true); // 忽略客户端断开连接
        ob_implicit_flush(true); // 开启隐式刷新
        // 这行代码使用循环来清空所有当前激活的输出缓冲区。ob_end_flush() 函数会刷新并关闭最内层的输出缓冲区，@ 符号用于抑制可能出现的错误或警告。
        try
        {
            while (ob_get_level() > 0 && @ob_end_flush())
            {
            }
        }
        catch (\Exception $e)
        {
        }
        // 这行代码设置 HTTP 响应的 Content-Type 为 text/event-stream，这是服务器发送事件（SSE）的 MIME 类型。
        header('Content-Type: text/event-stream');

        // 这行代码设置 HTTP 响应的 Cache-Control 为 no-cache，告诉浏览器不要缓存此响应。
        header('Cache-Control: no-cache');

        // 这行代码设置 HTTP 响应的 Connection 为 keep-alive，保持长连接，以便服务器可以持续发送事件到客户端。
        header('Connection: keep-alive');

        header('X-Accel-Buffering: no');//这一行必须存在，否则不好使（X-Accel-Buffering 头信息设置为“no”，表示已经禁用缓冲机制，服务器会立即发送响应数据，以确保客户端能够尽早地接收到数据。而如果设置为“yes”或不设置该头信息，则表示启用缓冲机制，服务器会将响应数据写入缓冲区，然后批量发送给客户端。)
        // 创建一个 HTTP Client 实例
//        $client = new Client([
//            'timeout' => 60 // 超时时间60秒
//        ]);
//
//        // 调用 request 方法发送请求
//        $response = $client->request("POST", $url, [
//            'headers' => [
//                'Content-Type' => 'application/json'
//            ],
//            'json' => $chatParams, // 设置请求参数
//            'stream' => true // 设置流式输出
//        ]);
//
//        // 输出响应体
//        $stream = $response->getBody();
//
//        $oldStr = '';
//        $resStr = '';
//        $flag = 0;
//        while (!$stream->eof())
//        {
//            $res = $stream->read(1); // 每次读取 1 字节，可以根据需求进行调整
//            if ($res === "\n")
//            {
//                $flag++;
//            } else
//            {
//                $flag = 0;
//            }
//            $resStr .= $res;
//            if ($flag >= 4)
//            {   // 检测到3个 \n\n\n 则将数据 输出到页面
//                $resStr = rtrim($resStr, "\n\n\n\n");
//                if ($resStr)
//                {
//                    usleep(50000);
//                    echo $resStr; // 输出一波数据
//                    flush(); //刷新缓存（直接发送到浏览器）
//                }
//                $oldStr = $resStr;
//                $resStr = ''; // 置空
//                $flag = 0;
//            }
//        }
        $resData = Curl::requestStream($url, $chatParams, 'POST', ['Content-Type' => 'application/json', 'stream' => true], function ($stream)
        {
            $resStr = '';
            while (!$stream->eof())
            {
                $res = $stream->read(1); // 每次读取 1 字节，可以根据需求进行调整
                echo $res; // 输出一波数据
                flush(); //刷新缓存（直接发送到浏览器）

                $resStr .= $res;
            }

            return $resStr;
        }, 'json');
        $resData['question'] = $question;
        $resData['chat_id'] = $chatId;
        $resData['chat_record_id'] = $chatDetailId;
        // 添加记录
        Db::table('wechat_backup_07_18_14_43_00.hz_call_chat_python_result_log')->insert(['chat_id' => $chatId, 'chat_record_id' => $chatDetailId, 'result' => json_encode($resData, JSON_UNESCAPED_UNICODE), 'create_time' => time()]);

        if ($resData['success'])
        {
            return $resData['data'];
        } else
        {
            return '';
        }
    }

    /**
     * 获取 塔罗 解答结果
     * @param array $chatParams
     * @return mixed|string|true
     * @throws GuzzleException
     */
    public static function getTarotAiAnswerStreamData(array $chatParams)
    {
        $url = Env::get('haozhungpt.url') . '/api/get_dice_or_tarot_result';
        // 这行代码用于关闭输出缓冲。关闭后，脚本的输出将立即发送到浏览器，而不是等待缓冲区填满或脚本执行完毕。
//        ini_set('output_buffering', 'off');
        // 这行代码禁用了 zlib 压缩。通常情况下，启用 zlib 压缩可以减小发送到浏览器的数据量，但对于服务器发送事件来说，实时性更重要，因此需要禁用压缩。
//        ini_set('zlib.output_compression', false);
        // 这行代码使用循环来清空所有当前激活的输出缓冲区。ob_end_flush() 函数会刷新并关闭最内层的输出缓冲区，@ 符号用于抑制可能出现的错误或警告。
        try
        {
            while (ob_get_level() > 0 && @ob_end_flush())
            {
            }
        }
        catch (\Exception $e)
        {
//            \think\Log::write(__CLASS__ . ' => ' . __FUNCTION__ . '()异常：' . $e->getMessage() . '失败信息：' . $e->getFile() . ':' . $e->getLine(), 'error', true);
        }
        ignore_user_abort(true); // 忽略客户端断开连接
        ob_implicit_flush(true); // 开启隐式刷新
        // 这行代码设置 HTTP 响应的 Content-Type 为 text/event-stream，这是服务器发送事件（SSE）的 MIME 类型。
        header('Content-Type: text/event-stream');
        // 这行代码设置 HTTP 响应的 Cache-Control 为 no-cache，告诉浏览器不要缓存此响应。
        header('Cache-Control: no-cache');
        // 这行代码设置 HTTP 响应的 Connection 为 keep-alive，保持长连接，以便服务器可以持续发送事件到客户端。
        header('Connection: keep-alive');

        header('X-Accel-Buffering: no');//这一行必须存在，否则不好使（X-Accel-Buffering 头信息设置为“no”，表示已经禁用缓冲机制，服务器会立即发送响应数据，以确保客户端能够尽早地接收到数据。而如果设置为“yes”或不设置该头信息，则表示启用缓冲机制，服务器会将响应数据写入缓冲区，然后批量发送给客户端。)
        // 创建一个 HTTP Client 实例
        $resData = Curl::requestStream($url, $chatParams, 'POST', ['Content-Type' => 'application/json', 'stream' => true], static function ($stream)
        {
            $resStr = '';
            while (!$stream->eof())
            {
                $res = $stream->read(1); // 每次读取 1 字节，可以根据需求进行调整
                echo $res; // 输出一波数据
                flush(); //刷新缓存（直接发送到浏览器）
                $resStr .= $res;
            }
            return $resStr;
        }, 'json');

        if ($resData['success'])
        {
            return $resData['data'];
        }
        return '';
    }

    /**
     * 获取 文本转语音
     * @param $chatParams
     * @return string
     */
    public static function getChatTTS($chatParams)
    {
        $url = Env::get('haozhungpt.url') . '/get_text2voice';
        // 这行代码用于关闭输出缓冲。关闭后，脚本的输出将立即发送到浏览器，而不是等待缓冲区填满或脚本执行完毕。
//        ini_set('output_buffering', 'off');
        // 这行代码禁用了 zlib 压缩。通常情况下，启用 zlib 压缩可以减小发送到浏览器的数据量，但对于服务器发送事件来说，实时性更重要，因此需要禁用压缩。
//        ini_set('zlib.output_compression', false);
        // 这行代码使用循环来清空所有当前激活的输出缓冲区。ob_end_flush() 函数会刷新并关闭最内层的输出缓冲区，@ 符号用于抑制可能出现的错误或警告。
        try
        {
            while (ob_get_level() > 0 && @ob_end_flush())
            {
            }
        }
        catch (\Exception $e)
        {
//            \think\Log::write(__CLASS__ . ' => ' . __FUNCTION__ . '()异常：' . $e->getMessage() . '失败信息：' . $e->getFile() . ':' . $e->getLine(), 'error', true);
        }
        ignore_user_abort(true); // 忽略客户端断开连接
        ob_implicit_flush(true); // 开启隐式刷新
        // 这行代码设置 HTTP 响应的 Content-Type 为 text/event-stream，这是服务器发送事件（SSE）的 MIME 类型。
        header('Content-Type: audio/wav');
        // 这行代码设置 HTTP 响应的 Cache-Control 为 no-cache，告诉浏览器不要缓存此响应。
        header('Cache-Control: no-cache');
        // 这行代码设置 HTTP 响应的 Connection 为 keep-alive，保持长连接，以便服务器可以持续发送事件到客户端。
        header('Connection: keep-alive');

        header('X-Accel-Buffering: no');//这一行必须存在，否则不好使（X-Accel-Buffering 头信息设置为“no”，表示已经禁用缓冲机制，服务器会立即发送响应数据，以确保客户端能够尽早地接收到数据。而如果设置为“yes”或不设置该头信息，则表示启用缓冲机制，服务器会将响应数据写入缓冲区，然后批量发送给客户端。)
        // 创建一个 HTTP Client 实例
        $resData = Curl::requestStream($url, $chatParams, 'GET', ['Content-Type' => 'application/json', 'stream' => true], static function ($stream)
        {
            $resStr = '';
            while (!$stream->eof())
            {
                $res = $stream->read(1); // 每次读取 1 字节，可以根据需求进行调整
                echo $res; // 输出一波数据
                flush(); //刷新缓存（直接发送到浏览器）
                $resStr .= $res;
            }
            return $resStr;
        }, 'json');

        if ($resData['success'])
        {
            return $resData['data'];
        }
        return '';
    }

    public static function sendVoice($content)
    {
        try
        {
            while (ob_get_level() > 0 && @ob_end_flush())
            {
            }
        }
        catch (\Exception $e)
        {
//            \think\Log::write(__CLASS__ . ' => ' . __FUNCTION__ . '()异常：' . $e->getMessage() . '失败信息：' . $e->getFile() . ':' . $e->getLine(), 'error', true);
        }
        // 这行代码设置 HTTP 响应的 Content-Type 为 text/event-stream，这是服务器发送事件（SSE）的 MIME 类型。
        header('Content-Type: audio/wav');
        // 这行代码设置 HTTP 响应的 Cache-Control 为 no-cache，告诉浏览器不要缓存此响应。
        header('Cache-Control: no-cache');
        // 这行代码设置 HTTP 响应的 Connection 为 keep-alive，保持长连接，以便服务器可以持续发送事件到客户端。
        header('Connection: keep-alive');

        header('X-Accel-Buffering: no');//这一行必须存在，否则不好使（X-Accel-Buffering 头信息设置为“no”，表示已经禁用缓冲机制，服务器会立即发送响应数据，以确保客户端能够尽早地接收到数据。而如果设置为“yes”或不设置该头信息，则表示启用缓冲机制，服务器会将响应数据写入缓冲区，然后批量发送给客户端。)

        echo $content;
    }

    /**
     * 取档案指定问题分析的结果 2.10.2好准嵌入新增 词条问题 及 数据库 取的好准那边的数据库信息
     * @param int $qid
     * @param string $sign 提问星座
     * @param array|null $recodeInfo
     * @return array
     */
    public static function getAnalysisResult2(int $qid, $sign = '', $recodeInfo = null)
    {
        // 查询问题数据
        try
        {
//            $question = Db::table('haozhungpt_ai.hz_question')
//                ->field('question,need_recode,relation_type,ouput_type')
//                ->where(['id' => $qid, 'status' => 1])
//                ->findOrFail();
            $question = (new HzHzQuestion())->getInfoById($qid);
        }
        catch (\Exception $e)
        {
            // TODO 未查询到问题，异常处理
            $question = [];
        }

        // 查询该问题的逻辑数据
        try
        {
//            $questionLogic = Db::table('haozhungpt_api.hz_question_logic')
//                ->field('id,question_id,pid,relation_type,logic_type')
//                ->where(['question_id' => $qid, 'status' => 1])
//                ->order('pid asc,id asc')
//                ->select();
            $questionLogic = (new HzHzQuestionLogic())->batchGetInfoByIds($qid);
        }
        catch (\Exception $e)
        {
            // TODO 未查询到问题的逻辑，异常处理
            $questionLogic = [];
        }
        // 问题的逻辑数据格式重组
        $logic = [
            'need_recode' => $question['need_recode'], // 是否需要档案
            'relation' => $question['relation_type'], // 一级逻辑的逻辑关系  1=>或, 2=>且
            'output_type' => $question['ouput_type'], // 词条输出方式：0=>优先,1=>合并,2=>全部
            'list' => [],
        ];
        $logicIds = [];
        foreach ($questionLogic as $k1 => $v1)
        {
            $logicIds[] = $v1['id'];
            // 一级逻辑
            if ($v1['pid'] === 0)
            {
                $logic['list'][$v1['id']] = [
                    'type' => $v1['logic_type'],
                    'relation' => $v1['relation_type'],
                    'list' => [],
                    'text1' => [],
                    'text2' => [],
                ];
            } else
            {
                // 二级逻辑
                $logic['list'][$v1['pid']]['list'][$v1['id']] = [
                    'type' => $v1['logic_type'],
                ];
            }
        }

        // 查询逻辑下所有词条
        try
        {
//            $logicTextList = Db::table('haozhungpt_api.hz_question_text')
//                ->field('id,logic_id,specific_value')
//                ->where(['logic_id' => ['in', $logicIds], 'status' => 1])
//                ->order('id asc')
//                ->select();
            $logicTextList = (new HzHzQuestionText())->batchGetDataByLogicId($logicIds);
        }
        catch (\Exception $e)
        {
            // TODO 逻辑下没有词条的异常处理
            $logicTextList = [];
        }
        // 将词条组进逻辑中
        foreach ($logic['list'] as $k2 => $v2)
        {
            foreach ($logicTextList as $k3 => $v3)
            {
                if ($k2 === $v3['logic_id'])
                {
                    $v3['logic_perfix'] = $v2['type'];
                    $logic['list'][$k2]['text1'][] = $v3;
                } else
                {
                    if (isset($v2['list'][$v3['logic_id']]))
                    {
                        $v3['logic_perfix'] = $v2['list'][$v3['logic_id']]['type'];
                        $logic['list'][$k2]['text2'][] = $v3;
                    }
                }
            }
        }
        $textList = [];
        $natalData = [];
        // 若该问题需要档案
        if ($logic['need_recode'] === 1)
        {
            // 固定星盘配置，开启所有星体
            $setting = [
                'natal' => [
                    'asp_orb_all' => [0 => 8, 60 => 6, 90 => 7, 120 => 7, 180 => 7],
                    'asps_display' => [0, 60, 90, 120, 180],
                    'planets_display' => '111111111111111111111',
                    'planet_orb_all' => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    'h_sys' => 'p',
                ],
            ];
            // 获取本命盘数据
            $natalData = Astrolog::getInstance()
                ->setRecode1($recodeInfo)
                ->setExecPrefix(config('astrolog.prefix'))
                ->setIsAstrolabe(false)
                ->setUserSetting($setting)
                ->setGetDatas(['natal'])
                ->getAstroDatas()
                ->getDatas('natal');
        }
        foreach ($logic['list'] as $k4 => $v4)
        {
            // 遍历1级逻辑
            foreach ($v4['text1'] as $k5 => $v5)
            {
                /**
                 * 若输出方式为优先输出
                 *   1级逻辑关系为或
                 *      2级逻辑关系为或
                 *        检测1级逻辑及其子逻辑，满足条件的都要输出，并结束后续检测
                 *      2级逻辑关系为且
                 *        检测1级逻辑及其子逻辑，所有条件全部满足才输出，并结束后续检测
                 *   1级逻辑关系为且
                 *      2级逻辑关系为或
                 *        检测1级逻辑及其子逻辑，满足条件的都要输出，并结束后续检测
                 *      2级逻辑关系为且
                 *        检测1级逻辑及其子逻辑，所有条件全部满足才输出，并结束后续检测
                 * 若输出方式为全部输出
                 *   1级逻辑关系为或
                 *      2级逻辑关系为或
                 *        检测1级逻辑及其子逻辑，满足条件的都要输出，并继续检测
                 *      2级逻辑关系为且
                 *        检测1级逻辑及其子逻辑，所有条件全部满足才输出，并继续检测
                 *   1级逻辑关系为且
                 *      2级逻辑关系为或
                 *        检测1级逻辑及其子逻辑，满足条件的都要输出，并继续检测
                 *      2级逻辑关系为且
                 *        检测1级逻辑及其子逻辑，所有条件全部满足才输出，并继续检测
                 * 若输出方式为合并输出
                 *   暂不支持
                 */
                switch ($logic['output_type'])
                {
                    case 0:
                        // 优先输出
                        if (!empty($textList))
                        {
                            break 3;
                        }
                        $option1 = [
                            'logic' => $v5['logic_perfix'] . ':' . $v5['specific_value'], // 逻辑字符串
                            'natal_data' => $natalData, // 本命盘数据
                            'sign' => $sign,
                            'need_recode' => $logic['need_recode'],
                        ];
                        // 检测一级逻辑是否满足
                        $matchingResult1 = self::isLogicMatching($option1);
                        if ($matchingResult1)
                        {
                            $textList[] = $v5['id'];
                            break 3;
                        }
                        // 遍历二级逻辑
                        if (isset($v4['text2']) && !empty($v4['text2']))
                        {
                            foreach ($v4['text2'] as $k6 => $v6)
                            {
                                $option2 = [
                                    'logic' => $v6['logic_perfix'] . ':' . $v6['specific_value'], // 逻辑字符串
                                    'natal_data' => $natalData, // 本命盘数据
                                    'sign' => $sign,
                                    'need_recode' => $logic['need_recode'],
                                ];
                                $matchingResult2 = self::isLogicMatching($option2);
                                if ($matchingResult2)
                                {
                                    $textList[] = $v6['id'];
                                    break 4;
                                }
                            }
                        }
                        break;
                    case 2:
                        // 全部输出
//                            $textList[] = $v5['id'];
//                            break;
                    case 1:
                        // 合并输出
                        $option1 = [
                            'logic' => $v5['logic_perfix'] . ':' . $v5['specific_value'], // 逻辑字符串
                            'natal_data' => $natalData, // 本命盘数据
                            'sign' => $sign,
                            'need_recode' => $logic['need_recode'],
                        ];
                        // 检测一级逻辑是否满足
                        $matchingResult1 = self::isLogicMatching($option1);
                        if ($matchingResult1)
                        {
                            $textList[] = $v5['id'];
                        }
                        break;
                    default:
                        // 当前不支持的输出方式

                        break;
                }
            }
            if ($logic['output_type'] !== 0)
            {
                // 遍历二级逻辑
                if (isset($v4['text2']) && !empty($v4['text2']))
                {
                    foreach ($v4['text2'] as $k6 => $v6)
                    {
                        $option2 = [
                            'logic' => $v6['logic_perfix'] . ':' . $v6['specific_value'], // 逻辑字符串
                            'natal_data' => $natalData, // 本命盘数据
                            'sign' => $sign,
                            'need_recode' => $logic['need_recode'],
                        ];
                        $matchingResult2 = self::isLogicMatching($option2);
                        if ($matchingResult2)
                        {
                            $textList[] = $v6['id'];
                        }
                    }
                }
            }
        }

        if ($logic['output_type'] === 1 && !empty($textList) && count($textList) > 2)
        {
            // 若为合并输出，则随机取两条
            $tids = array_rand($textList, 2);

            $ts = [];
            foreach ($tids as $tid)
            {
                $ts[] = $textList[$tid];
            }
            $textList = $ts;
        }

        return $textList;
    }

    /**
     * 取档案指定问题分析的结果
     * @param array $question
     * @param string $sign 提问星座
     * @param array|null $recodeInfo
     * @return array
     */
    public static function getAnalysisResult(array $question, $sign = '', $recodeInfo = null)
    {
        // 查询该问题的逻辑数据
        try
        {
//            $questionLogic = Db::table('haozhungpt_api.hz_question_logic')
//                ->field('id,question_id,pid,relation_type,logic_type')
//                ->where(['question_id' => $qid, 'status' => 1])
//                ->order('pid asc,id asc')
//                ->select();
            $questionLogic = (new HzQuestionLogic())->batchGetInfoByIds($question['id']);
        }
        catch (\Exception $e)
        {
            $questionLogic = [];
        }
        // 问题的逻辑数据格式重组
        $logic = [
            'need_recode' => $question['need_recode'], // 是否需要档案
            'relation' => $question['relation_type'], // 一级逻辑的逻辑关系  1=>或, 2=>且
            'output_type' => $question['ouput_type'], // 词条输出方式：0=>优先,1=>合并,2=>全部
            'list' => [],
        ];
        $logicIds = [];
        foreach ($questionLogic as $k1 => $v1)
        {
            $logicIds[] = $v1['id'];
            // 一级逻辑
            if ($v1['pid'] === 0)
            {
                $logic['list'][$v1['id']] = [
                    'type' => $v1['logic_type'],
                    'relation' => $v1['relation_type'],
                    'list' => [],
                    'text1' => [],
                    'text2' => [],
                ];
            } else
            {
                // 二级逻辑
                $logic['list'][$v1['pid']]['list'][$v1['id']] = [
                    'type' => $v1['logic_type'],
                ];
            }
        }

        // 查询逻辑下所有词条
        try
        {
//            $logicTextList = Db::table('haozhungpt_api.hz_question_text')
//                ->field('id,logic_id,specific_value')
//                ->where(['logic_id' => ['in', $logicIds], 'status' => 1])
//                ->order('id asc')
//                ->select();
            $logicTextList = (new HzQuestionText())->batchGetDataByLogicId($logicIds);
        }
        catch (\Exception $e)
        {
            $logicTextList = [];
        }
        // 将词条组进逻辑中
        foreach ($logic['list'] as $k2 => $v2)
        {
            foreach ($logicTextList as $k3 => $v3)
            {
                if ($k2 === $v3['logic_id'])
                {
                    $v3['logic_perfix'] = $v2['type'];
                    $logic['list'][$k2]['text1'][] = $v3;
                } else
                {
                    if (isset($v2['list'][$v3['logic_id']]))
                    {
                        $v3['logic_perfix'] = $v2['list'][$v3['logic_id']]['type'];
                        $logic['list'][$k2]['text2'][] = $v3;
                    }
                }
            }
        }
        $textList = [];
        $natalData = [];
        // 若该问题需要档案
        if ($logic['need_recode'] === 1)
        {
            // 固定星盘配置，开启所有星体
            $setting = [
                'natal' => [
                    'asp_orb_all' => [0 => 8, 60 => 6, 90 => 7, 120 => 7, 180 => 7],
                    'asps_display' => [0, 60, 90, 120, 180],
                    'planets_display' => '111111111111111111111',
                    'planet_orb_all' => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    'h_sys' => 'p',
                ],
            ];
            // 获取本命盘数据
            $natalData = Astrolog::getInstance()
                ->setRecode1($recodeInfo)
                ->setExecPrefix(config('astrolog.prefix'))
                ->setIsAstrolabe(false)
                ->setUserSetting($setting)
                ->setGetDatas(['natal'])
                ->getAstroDatas()
                ->getDatas('natal');
        }
        foreach ($logic['list'] as $k4 => $v4)
        {
            // 遍历1级逻辑
            foreach ($v4['text1'] as $k5 => $v5)
            {
                /**
                 * 若输出方式为优先输出
                 *   1级逻辑关系为或
                 *      2级逻辑关系为或
                 *        检测1级逻辑及其子逻辑，满足条件的都要输出，并结束后续检测
                 *      2级逻辑关系为且
                 *        检测1级逻辑及其子逻辑，所有条件全部满足才输出，并结束后续检测
                 *   1级逻辑关系为且
                 *      2级逻辑关系为或
                 *        检测1级逻辑及其子逻辑，满足条件的都要输出，并结束后续检测
                 *      2级逻辑关系为且
                 *        检测1级逻辑及其子逻辑，所有条件全部满足才输出，并结束后续检测
                 * 若输出方式为全部输出
                 *   1级逻辑关系为或
                 *      2级逻辑关系为或
                 *        检测1级逻辑及其子逻辑，满足条件的都要输出，并继续检测
                 *      2级逻辑关系为且
                 *        检测1级逻辑及其子逻辑，所有条件全部满足才输出，并继续检测
                 *   1级逻辑关系为且
                 *      2级逻辑关系为或
                 *        检测1级逻辑及其子逻辑，满足条件的都要输出，并继续检测
                 *      2级逻辑关系为且
                 *        检测1级逻辑及其子逻辑，所有条件全部满足才输出，并继续检测
                 * 若输出方式为合并输出
                 *   暂不支持
                 */
                switch ($logic['output_type'])
                {
                    case 0:
                        // 优先输出
                        if (!empty($textList))
                        {
                            break 3;
                        }
                        $option1 = [
                            'logic' => $v5['logic_perfix'] . ':' . $v5['specific_value'], // 逻辑字符串
                            'natal_data' => $natalData, // 本命盘数据
                            'sign' => $sign,
                            'need_recode' => $logic['need_recode'],
                        ];
                        // 检测一级逻辑是否满足
                        $matchingResult1 = self::isLogicMatching($option1);
                        if ($matchingResult1)
                        {
                            $textList[] = $v5['id'];
                            break 3;
                        }
                        // 遍历二级逻辑
                        if (isset($v4['text2']) && !empty($v4['text2']))
                        {
                            foreach ($v4['text2'] as $k6 => $v6)
                            {
                                $option2 = [
                                    'logic' => $v6['logic_perfix'] . ':' . $v6['specific_value'], // 逻辑字符串
                                    'natal_data' => $natalData, // 本命盘数据
                                    'sign' => $sign,
                                    'need_recode' => $logic['need_recode'],
                                ];
                                $matchingResult2 = self::isLogicMatching($option2);
                                if ($matchingResult2)
                                {
                                    $textList[] = $v6['id'];
                                    break 4;
                                }
                            }
                        }
                        break;
                    case 2:
                        // 全部输出
//                            $textList[] = $v5['id'];
//                            break;
                    case 1:
                        // 合并输出
                        $option1 = [
                            'logic' => $v5['logic_perfix'] . ':' . $v5['specific_value'], // 逻辑字符串
                            'natal_data' => $natalData, // 本命盘数据
                            'sign' => $sign,
                            'need_recode' => $logic['need_recode'],
                        ];
                        // 检测一级逻辑是否满足
                        $matchingResult1 = self::isLogicMatching($option1);
                        if ($matchingResult1)
                        {
                            $textList[] = $v5['id'];
                        }
                        break;
                    default:
                        // 当前不支持的输出方式

                        break;
                }
            }
            if ($logic['output_type'] !== 0)
            {
                // 遍历二级逻辑
                if (isset($v4['text2']) && !empty($v4['text2']))
                {
                    foreach ($v4['text2'] as $k6 => $v6)
                    {
                        $option2 = [
                            'logic' => $v6['logic_perfix'] . ':' . $v6['specific_value'], // 逻辑字符串
                            'natal_data' => $natalData, // 本命盘数据
                            'sign' => $sign,
                            'need_recode' => $logic['need_recode'],
                        ];
                        $matchingResult2 = self::isLogicMatching($option2);
                        if ($matchingResult2)
                        {
                            $textList[] = $v6['id'];
                        }
                    }
                }
            }
        }

        if ($logic['output_type'] === 1 && !empty($textList) && count($textList) > 2)
        {
            // 若为合并输出，则随机取两条
            $tids = array_rand($textList, 2);

            $ts = [];
            foreach ($tids as $tid)
            {
                $ts[] = $textList[$tid];
            }
            $textList = $ts;
        }

        return $textList;
    }

    /**
     * 检测当前逻辑用户是否匹配
     * @param array $option
     *  logic string 逻辑字符串
     *  natal_data string 本命盘数据
     *  sign string 提问的星座（无档案要求的问题，需要判断星座）
     * @return bool
     */
    private static function isLogicMatching($option = []): bool
    {
        $res = false;
        // 逻辑字符串拆分
        $logicArr = explode(':', $option['logic']);
        $signList = ['白羊座', '金牛座', '双子座', '巨蟹座', '狮子座', '处女座', '天秤座', '天蝎座', '射手座', '摩羯座', '水瓶座', '双鱼座'];
        $signId = array_search($option['sign'], $signList, true) + 1;
        switch ($logicArr[0])
        {
            case '0':
                // 行星落座
                $e1 = (int)$logicArr[1];
                $e2 = (int)$logicArr[2] + 1;
                if (($option['need_recode'] && $option['natal_data']['planets'][$e1]['in_sign_id'] === $e2)
                    || (!$option['need_recode'] && $e2 === $signId))
                {
                    $res = true;
                }
                break;
            case '1':
                // 行星落宫
                $e1 = (int)$logicArr[1];
                $e2 = (int)$logicArr[2] + 1;
                if ($option['need_recode'] && $option['natal_data']['planets'][$e1]['in_house_id'] === $e2)
                {
                    $res = true;
                }
                break;
            case '2':
                // 行星相位
                $e1 = (int)$logicArr[1]; // 行星ID 1
                $e2 = (int)$logicArr[2]; // 行星ID 2
                $e3 = $logicArr[3]; // 相位字符串-隔开
                if ($option['need_recode'])
                {
                    // e3相位拆分-
                    $e3Arr = explode('-', $e3);
                    $key = $e1 . '-' . $e2;
                    // 若为空相，则没有相位
                    if ((in_array(360, $e3Arr, false) && !isset($option['natal_data']['planets'][$e1]['phase'][$key]))
                        || (isset($option['natal_data']['planets'][$e1]['phase'][$key])
                            && in_array($option['natal_data']['planets'][$e1]['phase'][$key]['phase_e'], $e3Arr, false)))
                    {
                        $res = true;
                    }
                }
                break;
            case '3':
                // 宫内行星 若行星ID为99表示群星 宫内行星>=3
                $e1 = (int)$logicArr[1] + 1;
                $e2 = (int)$logicArr[2];
                if ($option['need_recode'])
                {
                    // 群星X宫
                    if ($e2 === 99)
                    {
                        $num = 0;
                        for ($i = 0; $i < 10; $i++)
                        {
                            if ($option['natal_data']['planets'][$i]['in_house_id'] === $e1)
                            {
                                $num++;
                            }
                        }
                        if ($num >= 3)
                        {
                            $res = true;
                        }
                    } elseif ($option['natal_data']['planets'][$e2]['in_house_id'] === $e1)
                    {
                        $res = true;
                    }
                }
                break;
            case '4':
                // 宫主飞星
                $e1 = (int)$logicArr[1] + 1;
                $e2 = (int)$logicArr[2] + 1;
                if ($option['need_recode'] && $option['natal_data']['houses'][$e1]['protect_ancient_inhouse_id'] === $e2)
                {
                    $res = true;
                }
                break;
            case '5':
                // 宫头星座
                $e1 = (int)$logicArr[1] + 1;
                $e2 = (int)$logicArr[2] + 1;
                if ($option['need_recode'] && $option['natal_data']['houses'][$e1]['in_sign_id'] === $e2)
                {
                    $res = true;
                }
                break;
            case '6':
                // 保底
                $res = true;
                break;
            case '7':
                // 宫主星
                $e1 = (int)$logicArr[1] + 1;
                $e2 = (int)$logicArr[2];
                if ($option['need_recode'] && $option['natal_data']['houses'][$e1]['protect_ancient_id'] === $e2)
                {
                    $res = true;
                }
                break;
            case '8':
                // 座内行星 若行星ID为99表示群星 座内行星>=3
                $e1 = (int)$logicArr[1] + 1;
                $e2 = (int)$logicArr[2];
                if ($option['need_recode'])
                {
                    // 群星X宫
                    if ($e2 === 99)
                    {
                        $num = 0;
                        for ($i = 0; $i < 10; $i++)
                        {
                            if ($option['natal_data']['planets'][$i]['in_sign_id'] === $e1)
                            {
                                $num++;
                            }
                        }
                        if ($num >= 3)
                        {
                            $res = true;
                        }
                    } elseif ($option['natal_data']['planets'][$e2]['in_sign_id'] === $e1)
                    {
                        $res = true;
                    }
                }
                break;
            case '9':
                // 宫主与宫主相位
                $e1 = (int)$logicArr[1] + 1; // 宫位ID 1
                $e2 = (int)$logicArr[2] + 1; // 宫位ID 2
                $e3 = $logicArr[3]; // 相位字符串-隔开
                if ($option['need_recode'])
                {
                    // e3相位拆分-
                    $e3Arr = explode('-', $e3);
                    $e1 = $option['natal_data']['houses'][$e1]['protect_ancient_id']; // 宫主星1
                    $e2 = $option['natal_data']['houses'][$e2]['protect_ancient_id']; // 宫主星2
                    $key = $e1 . '-' . $e2;
                    // 若为空相，则没有相位
                    if ((in_array(360, $e3Arr, false) && !isset($option['natal_data']['planets'][$e1]['phase'][$key]))
                        || (isset($option['natal_data']['planets'][$e1]['phase'][$key])
                            && in_array($option['natal_data']['planets'][$e1]['phase'][$key]['phase_e'], $e3Arr, false)))
                    {
                        $res = true;
                    }
                }
                break;
            case ' 10':
                // 宫主与行星相位
                $e1 = (int)$logicArr[1] + 1; // 宫位ID 1
                $e2 = (int)$logicArr[2]; // 行星 2
                $e3 = $logicArr[3]; // 相位字符串-隔开
                if ($option['need_recode'])
                {
                    // e3相位拆分-
                    $e3Arr = explode('-', $e3);
                    $e1 = $option['natal_data']['houses'][$e1]['protect_ancient_id']; // 宫主星
                    $key = $e1 . '-' . $e2;
                    // 若为空相，则没有相位
                    if ((in_array(360, $e3Arr, false) && !isset($option['natal_data']['planets'][$e1]['phase'][$key]))
                        || (isset($option['natal_data']['planets'][$e1]['phase'][$key])
                            && in_array($option['natal_data']['planets'][$e1]['phase'][$key]['phase_e'], $e3Arr, false)))
                    {
                        $res = true;
                    }
                }
                break;
        }
        return $res;
    }
}
